/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jcode.netty.core;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import com.jcode.netty.protocol.Packet;

/**
 * 响应Future对象
 *
 * @Author dreajay
 * @DateTime 2016年12月3日 上午11:05:37
 *
 */
public class ResponseFuture {
	private volatile Packet packet;
	private volatile boolean sendRequestOK = true;
	private volatile Throwable cause;
	private final long sequenceId;
	private final long timeoutMillis;
	private final ResponseCallback responseCallback;
	private final long beginTimestamp = System.currentTimeMillis();
	private final CountDownLatch countDownLatch = new CountDownLatch(1);
	private final SemaphoreReleaseOnlyOnce once;
	private final AtomicBoolean executeCallbackOnlyOnce = new AtomicBoolean(false);

	public ResponseFuture(long sequenceId, long timeoutMillis, ResponseCallback responseCallback, SemaphoreReleaseOnlyOnce once) {
		this.sequenceId = sequenceId;
		this.timeoutMillis = timeoutMillis;
		this.responseCallback = responseCallback;
		this.once = once;
	}

	public void executeInvokeCallback() {
		if (responseCallback != null) {
			if (this.executeCallbackOnlyOnce.compareAndSet(false, true)) {
				responseCallback.operationComplete(this);
			}
		}
	}

	public void release() {
		if (this.once != null) {
			this.once.release();
		}
	}

	public boolean isTimeout() {
		long diff = System.currentTimeMillis() - this.beginTimestamp;
		return diff > this.timeoutMillis;
	}

	public Packet waitResponse(final long timeoutMillis) throws InterruptedException {
		this.countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
		return this.packet;
	}

	public void putResponse(final Packet packet) {
		this.packet = packet;
		this.countDownLatch.countDown();
	}

	public long getBeginTimestamp() {
		return beginTimestamp;
	}

	public boolean isSendRequestOK() {
		return sendRequestOK;
	}

	public void setSendRequestOK(boolean sendRequestOK) {
		this.sendRequestOK = sendRequestOK;
	}

	public long getTimeoutMillis() {
		return timeoutMillis;
	}

	public ResponseCallback getResponseCallback() {
		return responseCallback;
	}

	public Throwable getCause() {
		return cause;
	}

	public void setCause(Throwable cause) {
		this.cause = cause;
	}

	public Packet getResponse() {
		return packet;
	}

	public void setResponse(Packet packet) {
		this.packet = packet;
	}

	public long getSequenceId() {
		return sequenceId;
	}

	@Override
	public String toString() {
		return "ResponseFuture [packet=" + packet + ", sendRequestOK=" + sendRequestOK + ", cause=" + cause + ", sequenceId=" + sequenceId + ", timeoutMillis=" + timeoutMillis + ", responseCallback=" + responseCallback + ", beginTimestamp=" + beginTimestamp + ", countDownLatch=" + countDownLatch + "]";
	}
}
